package com.googlecode.alfrescocachingserver;

import java.io.File;
import java.io.IOException;
import java.util.UUID;

import net.sf.ehcache.constructs.blocking.UpdatingCacheEntryFactory;

import org.apache.commons.configuration.Configuration;
import org.apache.commons.io.FileUtils;

import com.googlecode.alfrescocachingserver.model.CacheEntry;
import com.googlecode.alfrescocachingserver.model.WebScriptCacheKey;
import com.googlecode.alfrescocachingserver.model.WebScriptResponse;
import com.googlecode.alfrescocachingserver.services.CacheKeysMappingService;
import com.googlecode.alfrescocachingserver.services.WebScriptConnectorService;
import com.googlecode.alfrescocachingserver.util.GlobalConstants;

public class WebScriptCacheEntryFactory implements UpdatingCacheEntryFactory
{
    private Configuration configuration;
    private WebScriptConnectorService webScriptConnectorService;
    private CacheKeysMappingService cacheKeysMappingService;
    
    public WebScriptCacheEntryFactory(Configuration configuration, WebScriptConnectorService webScriptConnectorService, CacheKeysMappingService cacheKeysMappingService)
    {
        this.configuration = configuration;
        this.webScriptConnectorService = webScriptConnectorService;
        this.cacheKeysMappingService = cacheKeysMappingService;
    }
    
    @Override
    public Object createEntry(Object key) throws Exception
    {
        WebScriptCacheKey cacheKey = new WebScriptCacheKey((String)key);
        
        WebScriptResponse webScriptResponse = webScriptConnectorService.processRequest(
              cacheKey.getWebScriptUri(),
              cacheKey.getContentType(),
              null);
        
        cacheKeysMappingService.updateMapping(cacheKey, webScriptResponse);
        
        CacheEntry cacheEntry = getCacheEntry(cacheKey, webScriptResponse);
        
        return cacheEntry;
    }

    @Override
    public void updateEntryValue(Object key, Object value) throws Exception
    {
        // TODO:  handle content refresh based on a configurable time period
        //        (cache configurable???, asynchronous???)
    }
    
    private CacheEntry getCacheEntry(WebScriptCacheKey webScriptCacheKey, WebScriptResponse webScriptResponse) throws IOException
    {
        CacheEntry cacheEntry = null;
        boolean localStorageEnabled = configuration.getBoolean("webScriptLocalStorageEnabled", false);
        
        if (localStorageEnabled)
        {
            // Defaulting to 5 megabytes (5242880 bytes) 
            int localStorageSize = configuration.getInt("webScriptLocalStorageSize", 5242880);
            
            if (webScriptResponse.getResponseBytes().length > localStorageSize)
            {
                String basePath = configuration.getString("webScriptLocalStoragePath");
                
                StringBuilder buf = new StringBuilder();
                
                String filename = UUID.randomUUID().toString();
                
                String[] uuidFields = filename.split("-");
                
                for (int i = 0; i < uuidFields.length - 1; i++)
                {
                    buf.append(GlobalConstants.FILE_SEPARATOR);
                    buf.append(uuidFields[i]);
                }

                File fileDirectory = new File(basePath + buf.toString());

                if (!fileDirectory.exists())
                {
                    fileDirectory.mkdirs();
                }

                File file = new File(fileDirectory.getCanonicalPath() + GlobalConstants.FILE_SEPARATOR + filename);
                
                FileUtils.writeByteArrayToFile(file, webScriptResponse.getResponseBytes());
                
                String filePath = buf.toString() + GlobalConstants.FILE_SEPARATOR + filename;
                
                cacheEntry = new CacheEntry();
                cacheEntry.setLocalFilePath(filePath);
            }
        }
        
        if (cacheEntry == null)
        {
            cacheEntry = new CacheEntry();
            cacheEntry.setValue(webScriptResponse.getResponseBytes());
        }
        
        cacheEntry.setHeaderMap(webScriptResponse.getHeaderMap());
        
        return cacheEntry;
    }
}